//////////////////////////////////////////////////////////////////
//
//
//  Violation computation routines for the ISPD 2012 contest
//
//  This code is provided for description purposes only. The contest
//  organizers cannot guarantee that the provided code is free of
//  bugs or defects. !!!! USE THIS CODE AT YOUR OWN RISK !!!!!
//
//
//  The contestants are free to use these functions as-is or make
//  modifications. If the contestants choose to use the provided
//  code, they are responsible for making sure that it works as
//  expected.
//
//  The code provided here has no real or implied warranties.
//
//
////////////////////////////////////////////////////////////////////

#include <string.h>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstdlib>
#include <limits>
#include <cassert>
#include <cctype>
#include <cmath>
#include <map>
#include <set>

using namespace std; 

#include "parser_helper.h"
#include "extendedverilogparser.h"
#include "circuitgraph.h"

extern ExtendedVerilogParser* vpPtr;
extern CircuitGraph* graphInPtr;
//extern ExtendedVerilogParser vp;
//extern CircuitGraph graphIn;


// Rouding function
double myRound(double v, int digits){
        double myV = v*pow(10.0,digits);
        int intv = (int) round(myV);
        return intv/pow(10.0,digits);
}


class violation_ctrl{
private:
        map<string,double> cap;
        map<string,string> fanout;
        set<string> _po_set;

        // File names
        string _lib;
        string _sdc;
        string _v;
        string _sizes;
        string _spef;
        string _timing;
        string _slack_violation_file;
        string _slew_violation_file;
        string _cap_violation_file;
  
        // Cell info
        map<string,LibParserCellInfo> _cells;
        map<string,pair<string,string> > _drv_cells;

        // Timing constraints
        double _ck_period;
        string _ck_name;
        string _ck_port;
        map<string,double> _output_delay;
        double _max_transition;
        double _slew_violations;
        double _slack_violations;
        double _cap_violations;

        // Circuit info
        map<string,string> _type_map_orig;
        map<string, vector< pair< string,string> > > _pin_map_orig;
        map<string, double > _netcap;
        map<string,string> _type_map_new;
        map<string,string> _type_map_merged;

        // Power
        double _total_leakage_power;

public:
        violation_ctrl(string contest, string bench, string lib) ;
        void check_violations(double, double, ofstream& myFile, bool&, bool&, bool&,double&, double&, double&, double&);
        void read_sdc();
        void read_lib();
        void read_v();
        void read_sizes();
        void read_spef();
        void read_timing();

        void print_new_sizes(){
                cout << "Printing new sizes: " << endl;
                for(map<string,string>::iterator it=_type_map_new.begin();
                    it!=_type_map_new.end();it++){
                        cout << it->first << " - " << it->second << endl;
                }
        }

        void merge_sizes();
        // Check for max cap violations
        double check_max_cap();
        void compute_power();
  
};


/*int main(int argc, char *argv[]){

  if (argc != 3) {
  cout << "Usage: " << argv[0] << " benchmark_name library_file" << endl ;
  exit(0) ;
  }

  string bench = argv[1] ;
  string lib = argv[2] ;
  
  // Create violation ctrl object
  violation_ctrl vc(bench,lib) ;
  vc.check_violations();

  return 0;
  }*/


violation_ctrl::violation_ctrl(string contest, string bench, string lib) {
        // Populate file names

        _lib = lib ;
        _sdc = contest + "/" + bench + "/" +  bench + ".sdc" ;
        _v = contest + "/" + bench + "/" +  bench + ".v" ;
        _sizes = contest + "/" + bench + "/" +  bench + ".int.sizes" ;
        _spef = contest + "/" + bench + "/" +  bench + ".spef" ;
        _timing = contest + "/" + bench + "/" +  bench + ".timing" ;
  
        _slack_violation_file = contest + "/" + bench + "/" +  bench + ".slack_violations" ;
        _slew_violation_file =  contest + "/" + bench  + "/" +  bench + ".slew_violations" ;
        _cap_violation_file = contest + "/" + bench  + "/" +  bench + ".cap_violations" ;

        // Check if input files exists
        bool fail=false;

        if(!ifstream(_lib.c_str()).good()){
                fail=true;
                cerr << "File " << _lib << " not found." << endl;
        }
        if(!ifstream(_sdc.c_str()).good()){
                fail=true;
                cerr << "File " << _sdc << " not found." << endl;
        }
        if(!ifstream(_v.c_str()).good()){
                fail=true;
                cerr << "File " << _v << " not found." << endl;
        }
        if(!ifstream(_sizes.c_str()).good()){
                fail=true;
                cerr << "File " << _sizes << " not found." << endl;
        }
        if(!ifstream(_spef.c_str()).good()){
                fail=true;
                cerr << "File " << _spef << " not found." << endl;
        }
        if(!ifstream(_timing.c_str()).good()){
                fail=true;
                cerr << "File " << _timing << " not found." << endl;
        }

        if(fail)
                exit(0);
}

void violation_ctrl::check_violations(double firstTNS, double firstLeakage, ofstream& myFile, bool& noSlew, bool& noSlack, bool& noCap
                                      , double& slewViol, double& slackViol, double& capViol, double& leakage)
{

        // Initilize delay violations
        _slack_violations=0;
        _slew_violations=0;
        _cap_violations=0;
        _total_leakage_power=0;

        // reading spef file
        read_spef();

        // read sdc file and populate constraints
        read_sdc();

        // read lib file and populate constraints
        read_lib();

        // read verilog file
        read_v();

        // read new sizes
        read_sizes();  

        // Merge new sizes to the old sizes
        // this has to be done prior to checking violations
        merge_sizes();

        // read timing
        read_timing();

        // Check capacitance violations
        check_max_cap();

        // compute total leakage power
        compute_power();

        myFile << setiosflags(ios::fixed) << setprecision(2);
        // TNS
        myFile << -_slack_violations << "\t" ;
  
        // TNS decrease
        myFile << (firstTNS + _slack_violations)/ firstTNS << "\t" ;

        // Total Slew
        myFile <<  _slew_violations << "\t" ;
  
        // Total Cap
        myFile << _cap_violations << "\t" ;

        // Total Violation
        myFile <<  _slack_violations + _slew_violations +  _cap_violations << "\t" ;
		 
        // Total Leakage
        myFile << _total_leakage_power << "\t" ;
  
        // Leakage Increase
        myFile << (_total_leakage_power - firstLeakage ) / firstLeakage << "\t" ;
  
        if (_slew_violations <= 0.000001)
                noSlew = true ;
  	
        if (_slack_violations <= 0.000001)
                noSlack = true ;
  	
        if (_cap_violations <= 0.000001)
                noCap = true ;
  
        cout << setiosflags(ios::fixed) << setprecision(2);
        cout << "Total Slack violations: " << _slack_violations << "ps" <<endl;
        cout << "Total Slew violations: " << _slew_violations << "ps" << endl;
        cout << "Total Cap violations: " << _cap_violations << "fF" << endl;
        cout << "Total violations: " << _slack_violations + _slew_violations +  _cap_violations << endl; 
        cout << "Total Leakage Power: " << _total_leakage_power << "uW" <<endl;
  
        slewViol = _slew_violations ;
        slackViol = _slack_violations ;
        capViol = _cap_violations	;
        leakage = _total_leakage_power ;
        
        int mode;
        if(slackViol > slewViol && slackViol > capViol)
                graphInPtr->mode = 3;
        else if(slewViol > slackViol && slewViol > capViol)
                graphInPtr->mode = 2;
        else if(capViol > slackViol && capViol > slewViol)
                graphInPtr->mode = 1;
        else
                graphInPtr->mode = 0;
        
  
}

void violation_ctrl::read_lib(){

        LibParser lp (_lib) ;

        _max_transition = 0.0 ;
        bool valid = lp.read_default_max_transition(_max_transition) ;
        assert (valid) ;

        int readCnt = 0 ;
        do {
                LibParserCellInfo cell ;
                valid = lp.read_cell_info (cell) ;

                if (valid) {
                        ++readCnt ;

                        // Add each cell to a cell map indexed by cellname
                        _cells.insert(make_pair(cell.name,cell));
                }
    
        } while (valid) ;

}

void violation_ctrl::read_sdc(){

        SdcParser sp (_sdc) ;
        bool valid = sp.read_clock (_ck_name, _ck_port, _ck_period) ;

        assert(valid) ;
        do {
                string portName ;
                double delay ;

                valid = sp.read_input_delay (portName, delay) ;

        } while (valid) ;
  

        do {
                string portName ;
                string driverSize ;
                string driverPin ;
                double inputTransitionFall ;
                double inputTransitionRise ;
    
                valid = sp.read_driver_info (portName, driverSize, driverPin,
                                             inputTransitionFall, inputTransitionRise) ;

                if(valid){
                        _drv_cells.insert( make_pair(portName,make_pair(driverSize,driverPin)));
                }
    
        } while (valid) ;

        do {
                string portName ;
                double delay ;

                valid = sp.read_output_delay (portName, delay) ;

                if (valid){
                        _output_delay.insert(pair<string,double> (portName,delay));
                }
        } while (valid) ;

  
        do {
                string portName ;
                double load ;

                valid = sp.read_output_load (portName, load) ;

                assert(load>=0);
                if(_netcap.find(portName) != _netcap.end()){
                        // Multiple occurances are possible because output ports will have capacitances declared in the spef file also
                        _netcap[portName]+=load;
                } else {
                        _netcap[portName]=load;	  
                }

        } while (valid) ;
}

void violation_ctrl::read_v(){

        VerilogParser vp (_v) ;

        string moduleName ;
        bool valid = vp.read_module (moduleName) ;
        assert (valid) ;  

        do {
                string primaryInput ;
                valid = vp.read_primary_input (primaryInput) ;

        } while (valid) ;


        do {
                string primaryOutput ;
                valid = vp.read_primary_output (primaryOutput) ;
                _po_set.insert(primaryOutput);
        } while (valid) ;

        do {
                string net ;
                valid = vp.read_wire (net) ;

        } while (valid) ;
  
        do {
                string cellType, cellInst ;
                vector<std::pair<string, string> > pinNetPairs ;
    
                valid = vp.read_cell_inst (cellType, cellInst, pinNetPairs) ;

                if (valid) {
                        _type_map_orig.insert(pair<string,string> (cellInst,cellType));
                        _pin_map_orig.insert(pair<string,vector< pair<string,string> > > (cellInst,pinNetPairs));

                        /*
                          MUST NOT INCLUDE PIN CAPS AT THIS POINT AS SIZES WERE NOT APPLIED
	 
                          for (int i=0; i < pinNetPairs.size(); ++i) {
                          // traverse all pins in the cell
                          double pincap=-1;
                          for (int j=0; j < (_cells[cellType].pins).size(); ++j) {
                          if((_cells[cellType].pins[j]).name == pinNetPairs[i].first) {
                          pincap=(_cells[cellType]).pins[j].capacitance;
                          break;
                          }
                          }
                          assert(pincap>=0);
                          if(_netcap.find(pinNetPairs[i].second) != _netcap.end()){
                          _netcap[pinNetPairs[i].second]+=pincap;
                          } else {
                          _netcap[pinNetPairs[i].second]=pincap;	  
                          }
                          } 
                        */

                }
    
        } while (valid) ;
}  


void violation_ctrl::read_spef() {

        SpefParser sp (_spef) ;

        string net ;
        double cap ;
        bool valid = sp.read_net_cap (net, cap) ;

        while (valid) {
                //    cout << "Lumped cap of net " << net << " is " << cap << endl ;
                assert(cap>=0);
                if(_netcap.find(net) != _netcap.end()){
                        _netcap[net]+=cap;
                } else {
                        _netcap[net]=cap;	  
                }
                //    cout << "netcap: " <<  _netcap[net] << endl;
                valid = sp.read_net_cap (net, cap) ;
        }
  
}


void violation_ctrl::read_sizes(){

        std::ifstream sizes_file(_sizes.c_str());
        string instance;
        string type;
        while(sizes_file >> instance >> type){
                //cout << "Instance: " << instance << " type: " << type << endl ; 
                _type_map_new.insert(pair<string,string> (instance,type));
        }

}


void violation_ctrl::read_timing() {

        TimingParser tp (_timing) ;
        ofstream slack_ofs (_slack_violation_file.c_str()) ;
        ofstream slew_ofs (_slew_violation_file.c_str()) ;
  
        bool valid = false ;
        // For each pin check slew constraints
        do {
    
                string cellInst, pin ;
                double riseSlack, fallSlack, riseTransition, fallTransition ;

                valid = tp.read_pin_timing (cellInst, pin, riseSlack, fallSlack,
                                            riseTransition, fallTransition) ;

                string cellName = "";

                if (valid) {
                        if(_type_map_merged.find(cellInst)!=_type_map_merged.end()){
                                cellName = _type_map_merged[cellInst];
                        } else {
                                cerr << "Warning: Cell instance " << cellInst << " in the timing file could not be found!"
                                     << endl ;
                        }
                        vertex_descriptor vi = vpPtr->instanceNodeMap[cellInst];
                        (*graphInPtr)[vi].Slack_Violation = 0.0;
                        (*graphInPtr)[vi].Slew_Violation = 0.0;
                        // If cell is sequential check slacks?
                        if(_cells[cellName].isSequential){
                                bool isInput=true;
                                bool pinFound=false;
                                for( unsigned int i=0;i<_cells[cellName].pins.size();++i){
                                        if(pin==_cells[cellName].pins[i].name){
                                                isInput=_cells[cellName].pins[i].isInput;
                                                pinFound = true ;
                                                break;
                                        }
                                }
                                if (!pinFound) {
                                        cerr << "Warning: Pin " << pin << " of cell " << cellName << " not found in the library!"
                                             << endl ;
                                }
        
                                // If pin is an input of a sequential element check
                                if(pinFound && isInput){
                                        double riseSlackV = myRound(min((double)0,riseSlack),2);
                                        double fallSlackV = myRound(min((double)0,fallSlack),2);

                                        if(fallSlackV<0){
                                                slack_ofs << cellInst << "/" << pin << "_f " << fallSlackV << endl ;
                                                (*graphInPtr)[vi].Slack_Violation += abs(fallSlackV);
                                                _slack_violations+=fallSlackV;
                                        }
                                        if(riseSlackV<0){
                                                slack_ofs << cellInst << "/" << pin << "_r " << riseSlackV << endl ;
                                                (*graphInPtr)[vi].Slack_Violation += abs(riseSlackV);
                                                _slack_violations+=riseSlackV;
                                        }
                                }
                        }

                        // Search for pin to find if this is an output pin
                        bool isInput=true;
                        bool pinFound = false ;
                        for( unsigned int i=0;i<_cells[cellName].pins.size();++i){
                                if(pin==_cells[cellName].pins[i].name){
                                        isInput=_cells[cellName].pins[i].isInput;
                                        pinFound = true ;
                                        break;
                                }
                        }
                        if (!pinFound) {
                                cerr << "Warning: Pin " << pin << " of cell " << cellName << " not found in the library!"
                                     << endl ;
                        }

      
                        if(pinFound && isInput){
                                double riseSlewV = myRound((double)min((double)0,(_max_transition-riseTransition)),2);
                                double fallSlewV = myRound((double)min((double)0,(_max_transition-fallTransition)),2);
                                if(riseSlewV<0){

                                        slew_ofs << cellInst << "/" << pin << "_r " << riseSlewV << endl ;
                                        (*graphInPtr)[vi].Slew_Violation += abs(riseSlewV);
                                        _slew_violations += riseSlewV;

                                }
                                if(fallSlewV<0){

                                        slew_ofs << cellInst << "/" << pin << "_f " << fallSlewV << endl ;
                                        (*graphInPtr)[vi].Slew_Violation += abs(fallSlewV);
                                        _slew_violations += fallSlewV;

                                }
                        }
                }

        } while (valid) ;

        // For each port check slacks and slews
        do {
    
                string port ;
                double riseSlack, fallSlack, riseTransition, fallTransition ;

                valid = tp.read_port_timing (port, riseSlack, fallSlack,
                                             riseTransition, fallTransition) ;

                if( (valid) && (_po_set.find(port) != _po_set.end())) {
                        vertex_descriptor vi = vpPtr->instanceNodeMap[port];
                        (*graphInPtr)[vi].Slack_Violation = 0.0;
                        (*graphInPtr)[vi].Slew_Violation = 0.0;
                        double riseSlackV = myRound(min((double)0,riseSlack),2);
                        double fallSlackV = myRound(min((double)0,fallSlack),2);

                        if(fallSlackV<0){
                                slack_ofs << port << "_f " << fallSlackV << endl ;
                                (*graphInPtr)[vi].Slack_Violation += abs(fallSlackV);
                                _slack_violations+=fallSlackV;
                        }
                        if(riseSlackV<0){
                                slack_ofs << port << "_r " << riseSlackV << endl ;
                                (*graphInPtr)[vi].Slack_Violation += abs(riseSlackV);
                                _slack_violations+=riseSlackV;
                        }

                        double riseSlewV = myRound((double)min((double)0,(_max_transition-riseTransition)),2);
                        double fallSlewV = myRound((double)min((double)0,(_max_transition-fallTransition)),2);
                        if(riseSlewV<0){

                                slew_ofs << port << "_r " << riseSlewV << endl;
                                (*graphInPtr)[vi].Slew_Violation += abs(riseSlewV);
                                _slew_violations += riseSlewV;

                        }
                        if(fallSlewV<0){

                                slew_ofs << port << "_f " << fallSlewV << endl;
                                (*graphInPtr)[vi].Slew_Violation += abs(fallSlewV);
                                _slew_violations += fallSlewV;

                        }
                }

        } while (valid) ;
}

  
void violation_ctrl::compute_power(){

        _total_leakage_power = 0;
        for(map<string,string>::iterator it=_type_map_merged.begin();
            it!=_type_map_merged.end();it++){    
                LibParserCellInfo c = _cells[it->second];
                assert(c.leakagePower>=0);
                _total_leakage_power += c.leakagePower;
        }

}


double violation_ctrl::check_max_cap(){

        _cap_violations = 0 ;
        ofstream cap_ofs (_cap_violation_file.c_str()) ;
  
        // Add all the pin caps to the right nets
        for(map<string,vector< pair<string,string> > >::iterator it=_pin_map_orig.begin();
            it!=_pin_map_orig.end();it++){

                string cellInst = it->first;
                // Get cell type from merge cell map to account for new sizes
                string cellType = _type_map_merged[cellInst];
                vector<std::pair<string, string> > pinNetPairs = it->second; ;
                // traverse all pins in the cell instance
                for (int i=0; i < pinNetPairs.size(); ++i) {
                        // get pin capacitance from .lib
                        double pincap=-1;
                        for (int j=0; j < (_cells[cellType].pins).size(); ++j) {
                                if((_cells[cellType].pins[j]).name == pinNetPairs[i].first) {
                                        pincap=(_cells[cellType]).pins[j].capacitance;
                                        break;
                                }
                        }
                        assert(pincap>=0);
      
                        if(_netcap.find(pinNetPairs[i].second) != _netcap.end()){
                                _netcap[pinNetPairs[i].second]+=pincap;
                        } else {
                                _netcap[pinNetPairs[i].second]=pincap;	  
                        }
                }
        }
  
        for(map<string,string>::iterator it=_type_map_merged.begin();
            it!=_type_map_merged.end();it++){
                // For each gate check the list of pins, for each pin check for max cap violations
                const vector< pair<string,string> >& pin_list = _pin_map_orig[it->first];
                // Make sure that it->first is a valid gate that has more than 1 pin
                assert(pin_list.size()>1);
    
                // get cell from library
                assert (_cells.find(it->second) != _cells.end()) ;
                LibParserCellInfo c = _cells[it->second];

                // Make sure the number of pins in the library cell is the same as in the verilog file
                assert(c.pins.size()==pin_list.size());
                vertex_descriptor vi = vpPtr->instanceNodeMap[it->first];
                (*graphInPtr)[vi].Cap_Violation = 0.0;
                // for each pin
                for(unsigned int i=0;i<pin_list.size();++i){
                        string pin_name = pin_list[i].first;
                        string net_name = pin_list[i].second;

                        assert (_netcap.find (net_name) != _netcap.end()) ;
                        double net_cap = _netcap[net_name];
                        assert(net_cap>=0);

                        double gate_max_cap=0;
                        bool isInput=false;
                        bool pinFound = false ;
                        for(unsigned int j=0;j<c.pins.size();++j){
                                if(c.pins[j].name == pin_name){
                                        gate_max_cap=c.pins[j].maxCapacitance;
                                        isInput=c.pins[j].isInput;
                                        pinFound = true ;
                                        break;
                                }
                        }
                        assert (pinFound) ;
      
                        // max capacitance violation are not checked for input nodes
                        if(!isInput){
                                double violation = myRound((double)min((double)0, gate_max_cap - net_cap), 2);
                                if(violation < 0){
                                        cap_ofs << it->first << "/" << pin_name << " " << violation << "\t" << it->second << endl ;
                                }
                                assert(violation<=0);
                                (*graphInPtr)[vi].Cap_Violation += abs(violation);
                                _cap_violations += violation;
                        }
                }
        }

        // Check each input driver
        for(map<string,pair<string,string> >::iterator it=_drv_cells.begin();
            it!=_drv_cells.end();it++){
    
                string net_name=it->first;
                string cell=(it->second).first;
                string pin_name=(it->second).second;
    
                vertex_descriptor vi = vpPtr->instanceNodeMap[it->first];
                (*graphInPtr)[vi].Cap_Violation = 0.0;
                
                // Total cap for this net
                assert (_netcap.find(net_name) != _netcap.end()) ;
                double net_cap=_netcap[net_name];
                assert (net_cap >= 0) ;

                // Cell capacitance limit
                assert (_cells.find(cell) != _cells.end()) ;
                LibParserCellInfo c = _cells[cell];

                double gate_max_cap=0;
                bool isInput=false;
                bool pinFound = false ;
                //      string pin_name;
                for(unsigned int j=0;j<c.pins.size();++j){
                        if(c.pins[j].name == pin_name){
                                gate_max_cap=c.pins[j].maxCapacitance;
                                isInput=c.pins[j].isInput;
                                pinFound=true ;
                                break;
                        }
                }
                assert (pinFound) ;
                assert(!isInput); // pin of the driving_cell should always be an output pin

                if(!isInput){
                        double violation = myRound((double) min((double)0, gate_max_cap-net_cap), 2);
                        if(violation < 0){
                                cap_ofs << it->first << "/" << pin_name << " " << violation << endl ;
                        }
                        assert(violation<=0);
                        (*graphInPtr)[vi].Cap_Violation += abs(violation);
                        _cap_violations += violation;
                }     
        }
}


void violation_ctrl::merge_sizes(){
        // Populate merged map with original sizes
        _type_map_merged=_type_map_orig;

        // traverse all the new sizes to update original sizes
        for(map<string,string>::iterator it=_type_map_new.begin();
            it!=_type_map_new.end();it++){
                // get original footprint
                string instance=it->first;
                string newcell_name=it->second;
                string originalcell_name=_type_map_orig[instance];
    
                string newfootprint;
                if(_cells.find(newcell_name) != _cells.end()){
                        newfootprint=_cells[newcell_name].footprint;
                        assert (!newfootprint.empty()) ;
                }
                else {
                        cerr << "Warning: Cell " << newcell_name << " is not in the library!"
                             << endl ;

                        continue ;
                }

                assert (_cells.find(originalcell_name) != _cells.end()) ;
                string originalfootprint=_cells[originalcell_name].footprint;
                assert (!originalfootprint.empty()) ;
    
                // If footprints are the same update cell else print warning
                if(newfootprint == originalfootprint){
                        _type_map_merged[instance]=newcell_name;
                } else {
                        cerr << "Warning: invalid swapping: " << originalcell_name << " with " << newcell_name << endl;
                }
        }
}
